using Glpt.Data;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace Glpt.Task
{
    /// <summary>
    /// 计算报表表达式类
    /// </summary>
    public class ComputeReportCompExp
    {
        private DataTable dtcomp = new DataTable();
        private object lockObj = new object();
        private List<string> comp_field_list = null;
        private int keep_value = -1;//-1:新值替换原值 0新值为零不替换原值 1原值累加新值
        private SqlCommand cmdsql = null;
        private List<DataRow> drc = new List<DataRow>();
        public void SetParams(String db_no, String table_name, String comp_fields, String group_fields, String s_keep_value, String del_sql, String insert_table, String insert_fields, String insert_where)
        {
            if (s_keep_value.Length > 0)
            {
                if (!int.TryParse(s_keep_value, out keep_value))
                    keep_value = -1;
            }
            comp_field_list = SplitString(comp_fields, ',');//分拆计算列
            if (comp_field_list.Count == 0) return;
            for (int i = 0; i < comp_field_list.Count; i++)
            {
                string comp_field = comp_field_list[i];
                if (comp_field.StartsWith("[") && comp_field.EndsWith("]")) comp_field_list[i] = comp_field.Substring(1, comp_field.Length - 2);
            }
            DataTable dt = new DataTable();
            DbItem dbi = BaseInterFace.DbList.GetByNo(int.Parse(db_no));
            if (dbi == null)
                throw new Exception("没有找到数据库编号" + db_no);
            string connstr = AbstractInterFace.ReplaceString(dbi.GetConnString(), AbstractInterFace.PublicLeftChar, AbstractInterFace.PublicRightChar);
            using (SqlConnection connsql = new SqlConnection(connstr))
            {
                if (connsql.State.Equals(ConnectionState.Closed)) connsql.Open();
                string sql = "";
                if (table_name.ToUpper().StartsWith("SELECT") || table_name.ToUpper().StartsWith("EXEC"))
                    sql = table_name;
                else//拼出要获取数据的SQL语句
                {
                    List<string> field_list = new List<string>();
                    field_list.Add("ROW_NO");
                    field_list.Add("COMP_EXP");
                    AddItem(field_list, comp_fields);
                    AddItem(field_list, group_fields);
                    AddItem(field_list, insert_fields);
                    foreach (string val in field_list)
                    {
                        if (sql.Length > 0) sql += ",";
                        if (!val.StartsWith("[") && !val.EndsWith("]")) sql += "[";
                        sql += val;
                        if (!val.StartsWith("[") && !val.EndsWith("]")) sql += "]";
                    }
                    sql = "SELECT " + sql + " FROM " + table_name;
                }
                cmdsql = new SqlCommand(sql, connsql);
                cmdsql.CommandTimeout = 0;
                using (SqlDataAdapter sqlada = new SqlDataAdapter(cmdsql))
                {
                    sqlada.Fill(dt);//把数据读入内存
                }
                if (dt.Columns.Count == 0) throw new Exception("没有获取到数据");
                List<string> group_field_list = new List<string>();
                Dictionary<string, DataRow[]> lst = new Dictionary<string, DataRow[]>();
                if (group_fields.Length > 0)//分组
                {
                    group_field_list = SplitString(group_fields, ',');
                    for (int i = 0; i < group_field_list.Count; i++)
                    {
                        string group_field = group_field_list[i];
                        if (group_field.StartsWith("[") && group_field.EndsWith("]")) group_field_list[i] = group_field.Substring(1, group_field.Length - 2);
                    }
                    foreach (DataRow dr in dt.Rows)
                    {
                        string val = "";
                        string sel = "";
                        foreach (string group_field in group_field_list)
                        {
                            if (val.Length > 0)
                            {
                                val += ",";
                                sel += " AND ";
                            }
                            val += dr[group_field].ToString();
                            sel += "[" + group_field + "]='" + dr[group_field].ToString() + "'";
                        }
                        if (!lst.ContainsKey(val))
                        {
                            DataRow[] drs = dt.Select(sel);
                            lst.Add(val, drs);
                        }
                    }
                }
                else
                    lst.Add("", dt.Select());
                if (lst.Count > 1)
                {
                    Exception parallelException = null;
                    Parallel.ForEach(lst.Keys, (group, state) =>//并行计算
                    {
                        try
                        {
                            DataRow[] drs = lst[group];
                            DoCompute(drs);
                        }
                        catch (Exception pex)
                        {
                            parallelException = pex;
                            state.Break();
                        }
                    });
                    if (parallelException != null) throw new Exception(parallelException.Message, parallelException);
                }
                else
                {
                    foreach (string group in lst.Keys)//逐组计算
                    {
                        DoCompute(lst[group]);
                    }
                }
                if (del_sql.Length > 0)
                {
                    cmdsql.CommandText = del_sql;
                    cmdsql.ExecuteNonQuery();
                }
                if (insert_table.Length == 0 || insert_fields.Length == 0)//无输出的表名或字段名，则更新数据表
                {
                    if (drc.Count == 0) return;
                    cmdsql.Parameters.Clear();
                    sql = "";
                    int iparam = 0;
                    for (; iparam < comp_field_list.Count; iparam++)
                    {
                        string param_name = "P" + iparam.ToString();
                        cmdsql.Parameters.Add(param_name, SqlDbType.VarChar, 255);
                        if (sql.Length > 0) sql += ",";
                        sql += "[" + comp_field_list[iparam] + "]" + "=@" + param_name;
                    }
                    cmdsql.Parameters.Add("P" + iparam.ToString(), SqlDbType.VarChar, 255);
                    sql = "UPDATE " + table_name + " SET " + sql + " WHERE ROW_NO=@P" + iparam.ToString();
                    for (int i = 0; i < group_field_list.Count; i++)
                    {
                        string param_name = "P" + (i + iparam + 1).ToString();
                        cmdsql.Parameters.Add(param_name, SqlDbType.VarChar, 255);
                        sql += " AND [" + group_field_list[i] + "]=@" + param_name;
                    }
                    cmdsql.CommandText = sql;
                    SqlTransaction tran = connsql.BeginTransaction();//启用事务，批量更新再提交
                    cmdsql.Transaction = tran;
                    try
                    {
                        foreach (DataRow dr in drc)//有变化的记录列表
                        {
                            iparam = 0;
                            for (; iparam < comp_field_list.Count; iparam++)
                            {
                                cmdsql.Parameters[iparam].Value = dr[comp_field_list[iparam]];
                            }
                            cmdsql.Parameters[iparam].Value = dr["ROW_NO"];
                            for (int i = 0; i < group_field_list.Count; i++)
                            {
                                cmdsql.Parameters[i + iparam + 1].Value = dr[group_field_list[i]];
                            }
                            cmdsql.ExecuteNonQuery();
                        }
                        tran.Commit();
                    }
                    catch
                    {
                        tran.Rollback();
                        throw;
                    }
                }
                else//把数据写入到输出表里
                {
                    DataRow[] result = dt.Select(insert_where);
                    if (result.Length == 0) return;
                    List<string> fields = SplitString(insert_fields, ',');
                    for (int i = 0; i < fields.Count; i++)
                    {
                        string field_name = fields[i];
                        if (field_name.StartsWith("[") && field_name.EndsWith("]")) fields[i] = field_name.Substring(1, field_name.Length - 2);
                    }
                    using (SqlBulkCopy sqlbulkcopy = new SqlBulkCopy(connsql, SqlBulkCopyOptions.UseInternalTransaction | SqlBulkCopyOptions.KeepNulls | SqlBulkCopyOptions.KeepIdentity, null))
                    {
                        sqlbulkcopy.DestinationTableName = insert_table;
                        for (int i = 0; i < fields.Count; i++)
                        {
                            sqlbulkcopy.ColumnMappings.Add(fields[i], fields[i]);
                        }
                        sqlbulkcopy.BulkCopyTimeout = 7200;
                        sqlbulkcopy.BatchSize = 100000;
                        sqlbulkcopy.WriteToServer(result);
                    }
                }
            }
        }

        private void DoCompute(DataRow[] drs)
        {
            while (true)
            {
                int icount = 0;
                foreach (DataRow dr in drs)
                {
                    string COMP_EXP = dr["COMP_EXP"].ToString();
                    string ROW_NO = dr["ROW_NO"].ToString();
                    if (COMP_EXP.Length == 0) continue;//没有表达式跳过
                    bool exist = false;
                    foreach (DataRow dr2 in drs)
                    {
                        if (dr2["COMP_EXP"].ToString().Length > 0 && COMP_EXP.Contains(dr2["ROW_NO"].ToString()))//如果存在表达式,并且还被表达式所依赖
                        {
                            exist = true;
                            break;
                        }
                    }
                    if (!exist)//表达式里的每个项都不存在依赖
                    {
                        icount++;
                        bool isadd = false;
                        foreach (string comp_field in comp_field_list)//逐个字段计算
                        {
                            object val = DBNull.Value;
                            decimal old_value = 0;
                            string str_old_value = dr[comp_field].ToString();
                            if (str_old_value.Length > 0) old_value = decimal.Parse(str_old_value);
                            if (COMP_EXP.Equals(" - "))//取负值
                            {
                                if (keep_value == 1)//1:原值累加新值
                                    val = 0 - old_value * 2;
                                else
                                    val = 0 - old_value;
                            }
                            else
                            {
                                string COMP_EXP_REP = COMP_EXP;//把表达式放到变量里
                                foreach (DataRow dr3 in drs)//把值替换到表达式里
                                {
                                    string ROW_NO3 = dr3["ROW_NO"].ToString();
                                    if (ROW_NO3.Length > 0 && COMP_EXP_REP.Contains(ROW_NO3))
                                    {
                                        string BB = dr3[comp_field].ToString();
                                        if (BB.Length == 0) BB = "0";
                                        if (BB.StartsWith("-"))
                                            COMP_EXP_REP = COMP_EXP_REP.Replace(ROW_NO3, "(" + BB + ") ");
                                        else
                                            COMP_EXP_REP = COMP_EXP_REP.Replace(ROW_NO3, BB + " ");
                                    }
                                }
                                try//计算表达式
                                {
                                    if (COMP_EXP_REP.EndsWith("/0 ") || COMP_EXP_REP.EndsWith("/0.0000 "))
                                        val = 0;
                                    else if (System.Text.RegularExpressions.Regex.Matches(COMP_EXP_REP, "[a-zA-Z]").Count > 0)//含有字母
                                    {
                                        lock (lockObj)
                                        {
                                            cmdsql.CommandText = "SELECT " + COMP_EXP_REP;//通过SQL来计算表达式
                                            val = cmdsql.ExecuteScalar();
                                        }
                                    }
                                    else
                                        val = dtcomp.Compute(COMP_EXP_REP, "");//直接计算表达式
                                }
                                catch (DivideByZeroException)
                                {
                                    val = 0;
                                }
                                catch (Exception ex)
                                {
                                    if (ex.Message.StartsWith("遇到以零作除数错误") || ex.Message.StartsWith("值对于 Decimal 太大或太小"))
                                        val = 0;
                                    else
                                    {
                                        if (ex.Message.StartsWith("转换溢出"))
                                            COMP_EXP_REP = "CAST(" + COMP_EXP_REP + " AS DECIMAL(38,8))";
                                        lock (lockObj)
                                        {
                                            cmdsql.CommandText = "SELECT " + COMP_EXP_REP;
                                            val = cmdsql.ExecuteScalar();
                                        }
                                    }
                                }
                            }
                            decimal new_value = 0;
                            string str_new_value = val.ToString();
                            if (str_new_value.Equals("NaN") || str_new_value.Equals("非数字") || str_new_value.EndsWith("无穷大"))
                            {
                                val = "0";
                                str_new_value = "0";
                            }
                            if (str_new_value.Length > 0) new_value = decimal.Parse(str_new_value);
                            bool isrep = true;
                            if (keep_value == 0 && new_value == 0 && old_value == 0)
                                isrep = false;
                            else if (keep_value == 1)//1:原值累加新值
                                new_value = old_value + new_value;
                            if (isrep)//替换新值
                            {
                                lock (dr.Table.Rows.SyncRoot)
                                {
                                    dr[comp_field] = new_value;
                                }
                                if (!isadd)//并且尚未加入列表
                                {
                                    if (new_value != old_value || str_new_value.Length > 0 && str_old_value.Length == 0 || str_new_value.Length == 0 && str_old_value.Length > 0)//值有变化的才加入列表
                                    {
                                        lock (lockObj)
                                        {
                                            drc.Add(dr);
                                        }
                                        isadd = true;
                                    }
                                }
                            }
                        }
                        lock (dr.Table.Rows.SyncRoot)
                        {
                            dr["COMP_EXP"] = DBNull.Value;
                        }
                    }
                }
                if (icount == 0)
                    break;
            }
        }

        /// <summary>
        /// 分拆参数字符串
        /// </summary>
        /// <param name="paramString">参数</param>
        /// <param name="separator">分隔符</param>
        /// <returns>字符串</returns>
        private List<string> SplitString(string paramString, char separator)
        {
            List<string> values = new List<string>();
            int ibegin = 0;
            bool isbegin = false;
            for (int i = 0; i < paramString.Length; i++)
            {
                bool is_last = (i == paramString.Length - 1);
                char chr = paramString[i];
                if (chr.Equals('[')) isbegin = true;
                else if (chr.Equals(']')) isbegin = false;
                if (chr.Equals(separator) && !isbegin)
                {
                    string val = paramString.Substring(ibegin, i - ibegin).Trim();
                    if (val.Length > 0) values.Add(val);
                    ibegin = i + 1;
                }
                if (is_last)
                {
                    string val = paramString.Substring(ibegin).Trim();
                    if (val.Length > 0) values.Add(val);
                }
            }
            return values;
        }
        private void AddItem(List<string> field_list, string paramString)
        {
            List<string> vals = SplitString(paramString, ',');
            foreach (string val in vals)
            {
                if (!field_list.Contains(val))
                    field_list.Add(val);
            }
        }
    }
}
